﻿using System;
using System.Data;
using System.Windows;
using System.Windows.Controls;
using DBSearch.Properties;
using Connector;
using System.ComponentModel;

namespace DBSearch
{
    /// <summary>
    /// Interaction logic for DbSearcherMainWindow.xaml
    /// This tool is written by Lukas Oestemer. There is no warrenty. All rights reserved.
    /// For bugs and issues contact Lukas.Oestemer@avande.com
    ///
    /// Features:
    ///     - searches values inside a database 
    ///     - Logs errors into a log file.
    /// </summary>
    /// </summary>
    /// <remarks>
    /// This solution requires WPFToolkit v3.5.40128.1 or above for the data grid.
    /// </remarks>
    public partial class DbSearcherMainWindow : Window
    {
        /// <summary>
        /// Connector for the database connection.
        /// </summary>
        private Connector _connector;

        /// <summary>
        /// DataSet storing the results of the database search
        /// </summary>
        private DataSet _result;

        /// <summary>
        /// Table with all types of the database
        /// </summary>
        private DataTable _dataTypes;

        /// <summary>
        /// Search start time
        /// </summary>
        private DateTime _start;

        /// <summary>
        /// Window initialization.
        /// </summary>
        public DbSearcherMainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Receives all database types from the db server.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConnectGetTypes_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                EnableSearch(false);

                Logger.LogInformation("Try to connect server and get types.");

                string dbProvider = DbProvider.SelectedValue.ToString();
                _connector = new Connector(dbProvider, ConnectionString.Text);
                _dataTypes = _connector.GetDataTypes();
                ApplyTypeView(true);

                ColumnType.DataContext = _dataTypes.DefaultView;

                EnableSearch(true);

                Logger.LogInformation("Finish connection and got types.");
            }
            catch (Exception ex)
            {
                 Logger.LogError(ex);
                MessageBox.Show("The following error occoures:\n" + Logger.CreateExceptionMessage(ex), "Error!");
            }
        }

        /// <summary>
        /// Starts the search of the values in the database
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Search_Click(object sender, RoutedEventArgs e)
        {
            ControlsEnabled(false);

            Logger.LogInformation("Begin async search of " + SearchCriteria.Text.Replace("\'", "\'\'"));
            ProgressRunning.IsIndeterminate = true;

            if (ColumnType.SelectedItem == null)
                return;

            string type = (bool)DotNetTypes.IsChecked ? ColumnType.SelectedItem.ToString() : ((DataRowView)ColumnType.SelectedItem).Row["TypeName"].ToString();

            _start = DateTime.Now;
            RequiredTime.Content = "";
            ProcessState.Content = "";

            _connector.SearchValueAsync(type, DotNetTypes.IsChecked ?? false, SearchCriteria.Text.Replace("\'", "\'\'"), LikeSearch.IsChecked ?? false, ProgressChanged, RunWorkerCompleted);

            // Without Background Worker call method
            //_result = _connector.SearchValue(type, SearchCriteria.Text.Replace("\'", "\'\'"), LikeSearch.IsChecked ?? false);
            // and place code of RunWorkerCompleted here

            Logger.LogInformation("End search method");
        }

        /// <summary>
        /// The asynchronous database search is done.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _result = (DataSet)e.Result;
            TableResult.DataContext = _result.Tables;
 
            if (_result.Tables.Count > 0)
                TableResult.SelectedIndex = 0;

            RequiredTime.Content = String.Format("Search time: {0:HH:mm:ss:ffff}", DateTime.Now - _start);
            ProgressRunning.IsIndeterminate = false;
            ControlsEnabled(true);
        }

        /// <summary>
        /// The work process changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (!(e.UserState is object[]))
                return;

            var userState = (object[])e.UserState;
            if (!(userState[0] is int) || !(userState[0] is int))
                return;

            var totalTables = (int)userState[0];
            var processedTables = (int)userState[1];
            double percent = 100.0 / totalTables * processedTables;
            ProcessState.Content = String.Format("{0}/{1} tables done ({2:0.00}%)", processedTables, totalTables, percent);
        }

        /// <summary>
        /// Disables and enables the gui before and after searching.
        /// </summary>
        /// <param name="isEnabled">TRUE: the gui is accessable, otherwise false</param>
        private void ControlsEnabled(bool isEnabled)
        {
            Search.IsEnabled =
            Connect.IsEnabled =
            SearchCriteria.IsEnabled =
            LikeSearch.IsEnabled =
            DbProvider.IsEnabled =
            ColumnType.IsEnabled =
            DotNetTypes.IsEnabled =
            ConnectionString.IsEnabled = isEnabled;
        }

        /// <summary>
        /// Disables and enables the search part of the gui.
        /// </summary>
        /// <param name="isEnabled">TRUE: the gui is accessable, otherwise false</param>
        private void EnableSearch(bool isEnabled)
        {
            Search.IsEnabled =
            SearchCriteria.IsEnabled =
            LikeSearch.IsEnabled =
            DotNetTypes.IsEnabled =
            ColumnType.IsEnabled = isEnabled;
        }

        /// <summary>
        /// Switches the view in the data grid to the selected table.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TableResult_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Results.DataContext = TableResult.SelectedValue;
        }

        /// <summary>
        /// Initalizes the database connection and providers.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            EnableSearch(false);

            DbProvider.ItemsSource = Settings.Default.DbProviders;
            DbProvider.SelectedIndex = Settings.Default.DefaultDbProviderIndex;
            ConnectionString.Text = Settings.Default.DefaultConnectionString;
        }

        private void DotNetTypes_Checked(object sender, RoutedEventArgs e)
        {
            ApplyTypeView(!(DotNetTypes.IsChecked ?? false));
        }

        private void ApplyTypeView(bool dbTypes)
        {
            if (dbTypes)
            {
                _dataTypes.DefaultView.Sort = "TypeName ASC";
                ColumnType.DataContext = _dataTypes;
                //if (_dataTypes.Rows.Count > 0)
                //    ColumnType.SelectedIndex = 0;
            }
            else
            {
                System.Collections.Generic.List<string> dnTypes = new System.Collections.Generic.List<string>();
                foreach(DataRow row in _dataTypes.Rows)
                    if(!dnTypes.Contains(row["DataType"].ToString()))
                        dnTypes.Add(row["DataType"].ToString());
                
                dnTypes.Sort();
                ColumnType.DataContext = dnTypes;
                //if (dnTypes.Count > 0)
                //    ColumnType.SelectedIndex = 0;
            }
        }
    }
}
